import { readFileSync } from "node:fs";
import { version } from "workerd";
import { logger } from "../logger";
import { generateEnvTypes } from ".";
import type { Entry } from "../deployment-bundle/entry";
import type { Config } from "@cloudflare/workers-utils";

// Checks the default location for a generated types file and compares if the
// recorded Env hash, workerd version or compat date and flags have changed
// compared to the current values in the config. Prompts user to re-run wrangler
// types if so.
export const checkTypesDiff = async (config: Config, entry: Entry) => {
	if (!entry.file.endsWith(".ts")) {
		return;
	}
	let maybeExistingTypesFileLines: string[];
	try {
		// Checking the default location only
		maybeExistingTypesFileLines = readFileSync(
			"./worker-configuration.d.ts",
			"utf-8"
		).split("\n");
	} catch {
		return;
	}
	const existingEnvHeader = maybeExistingTypesFileLines.find((line) =>
		line.startsWith("// Generated by Wrangler by running")
	);
	const maybeExistingHash =
		existingEnvHeader?.match(/hash: (?<hash>.*)\)/)?.groups?.hash;
	const previousStrictVars = existingEnvHeader?.match(
		/--strict-vars(=|\s)(?<result>true|false)/
	)?.groups?.result;
	const previousEnvInterface = existingEnvHeader?.match(
		/--env-interface(=|\s)(?<result>[a-zA-Z][a-zA-Z0-9_]*)/
	)?.groups?.result;

	let newEnvHeader: string | undefined;
	try {
		const { envHeader } = await generateEnvTypes(
			config,
			{ strictVars: previousStrictVars === "false" ? false : true },
			previousEnvInterface ?? "Env",
			"worker-configuration.d.ts",
			entry,
			new Map(),
			// don't log anything
			false
		);
		newEnvHeader = envHeader;
	} catch (e) {
		logger.error(e);
	}

	const newHash = newEnvHeader?.match(/hash: (?<hash>.*)\)/)?.groups?.hash;

	const existingRuntimeHeader = maybeExistingTypesFileLines.find((line) =>
		line.startsWith("// Runtime types generated with")
	);
	const newRuntimeHeader = `// Runtime types generated with workerd@${version} ${config.compatibility_date} ${config.compatibility_flags.sort().join(",")}`;

	const envOutOfDate = existingEnvHeader && maybeExistingHash !== newHash;
	const runtimeOutOfDate =
		existingRuntimeHeader && existingRuntimeHeader !== newRuntimeHeader;

	return envOutOfDate || runtimeOutOfDate;
};
